package com.wang.tool.monitor.aop;

import com.wang.tool.monitor.InvokeTemplate;
import com.wang.tool.monitor.LinkLogUtil;
import com.wang.tool.monitor.core.MonitorApi;
import com.wang.tool.monitor.core.MonitorInfo;
import com.wang.tool.monitor.fastover.FastoverInfo;
import com.wang.tool.monitor.translator.GlobalErrorTranslator;
import com.wang.tool.common.ErrorInfo;
import com.wang.tool.common.Pair;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author wangshaopeng
 * @Date 2022/5/10
 */
public class MonitorAdvice {

    /**
     * 配置表
     */
    private static final Map<String, Meta> METAS = new HashMap<>();
    public static final String TAG_PARSE_MONITOR_CONFIG = "ParseMonitorConfig";


    /**
     * 监控切面
     *
     * @param point
     * @return
     * @throws Throwable
     */
    public Object monitorAround(ProceedingJoinPoint point) throws Throwable {
        long begin = System.currentTimeMillis();

        //类,方法
        Class<?> targetType = point.getTarget().getClass();
        Method method = ((MethodSignature) point.getSignature()).getMethod();

        Object result = null;
        ErrorInfo error = null;
        //配置加载成功时
        Meta meta = null;
        MonitorInfo<Object> monitorInfo = null;
        try {
            try {
                //基础配置加载完成
                meta = getOrInitMeta(targetType, method);
                monitorInfo = MonitorApi.stepInMonitor(meta.getGroup(), meta.getBiz(), meta.getAction(), meta.getWeaver(), point.getArgs(), begin);
            } catch (Throwable e) {
                LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                        .v1(targetType.getSimpleName() + ":" + method.getName())
                        .v2("initMonitor")
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }
//            记录入参
            try {
                if (meta != null && meta.isPrintParam()) {
                    Map<String, Object> details = monitorInfo.getDetails();
                    if (details == null || !details.containsKey(MonitorInfo.PARAM)) {
                        AnnotationWeaver weaver = meta.getWeaver();
                        Object param = weaver == null ? monitorInfo.getParams() : weaver.fetchParam(monitorInfo.getParams());
                        if (param != null) {
                            monitorInfo.addDetailParam(param);
//                            monitorInfo.addShortDetailOnDebug(MonitorInfo.PARAM, param);
                        }
                    }
                }
            } catch (Throwable e) {
                LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                        .v1(targetType.getSimpleName() + ":" + method.getName())
                        .v2("printParam")
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }

            //fastover
            FastoverInfo fastover = monitorInfo == null ? null : monitorInfo.getFastoverInfo();
            if (fastover != null) {
                if (fastover.getFail() != null) {
                    monitorInfo.addTag(MonitorInfo.TAG_FASTOVER, "fastFail", null);
                    throw fastover.getFail();
                }
                //判断是否适配
                Pair<Boolean, ?> fitResult = fastover.resultIsFit(method.getReturnType());
                if (fitResult.getLeft()) {
                    monitorInfo.addTag(MonitorInfo.TAG_FASTOVER, "fastReturn", null);
                    return result = fitResult.getRight();
                }
                //不正常
                LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                        .v1(monitorInfo.getBiz() + ":" + monitorInfo.getAction())
                        .v2("fastReturnNotFit")
                        .addAttachment("returnType", method.getReturnType().getName())
                        .addAttachment("realType", fastover.getResult() == null ? null : fastover.getResult().getClass().getName())
                        .pointLink(monitorInfo.getLink())
                        .log();
            }
            return result = point.proceed();
        } catch (Throwable e) {
            //翻译异常
            error = GlobalErrorTranslator.translate(e);
            //这里为什么不提供异常抓取的扩展点?
            //切面能力有天然的缺陷,类内部切面丢失,所以切面仅做不影响流程核心逻辑的东西
            //这里如果提供了可以隐性异常的扩展,可能是一种误导
            //如果是入口出错了,把异常堆栈打出来
            if (monitorInfo != null && monitorInfo.isRoot()) {
                LinkLogUtil.newTagLog(InvokeTemplate.TAG_ERROR_ON_ENTRANCE)
                        .v1(meta.getBiz() + ":" + meta.getAction())
                        .v2(error.getErrorCode())
                        .pointLink(monitorInfo.getLink())
                        .log();
                LinkLogUtil.error(InvokeTemplate.TAG_ERROR_ON_ENTRANCE, e, "[{0}][{1}][{2}]", meta.getBiz() + ":" + meta.getAction(), error.getErrorCode(), error.getErrorMsg());
            }
            throw e;
        } finally {
//            记录结果
            try {
                if (meta != null && meta.isPrintResult()) {
                    AnnotationWeaver weaver = meta.getWeaver();
                    Object fetchResult = weaver == null ? result : weaver.fetchResult(result);
                    if (fetchResult != null) {
                        monitorInfo.addDetailResult(fetchResult);
                        //链路上不记录结果,太大了
//                        monitorInfo.addShortDetailOnDebug(MonitorInfo.RESULT, "结果详情请查看日志");
                    }
                }
            } catch (Throwable e) {
                LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                        .v1(targetType.getSimpleName() + ":" + method.getName())
                        .v2("printResult")
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }
            //步出
            MonitorApi.stepOutMonitor(monitorInfo, result, error, System.currentTimeMillis());
        }
    }


    /**
     * 取/刷新配置
     *
     * @param targetType 类 非空
     * @param method     方法 非空
     * @return 非空
     */
    private Meta getOrInitMeta(Class<?> targetType, Method method) {
        //类名#方法名[参数类型]
        String key = targetType.getName().concat("#").concat(method.getName()).concat(Arrays.toString(method.getParameterTypes()));
        return METAS.computeIfAbsent(key, k -> {
            //方法上注解
            Monitor monitor = AnnotationUtils.findAnnotation(method, Monitor.class);
            if (monitor == null) {
                LinkLogUtil.newTagLog(TAG_PARSE_MONITOR_CONFIG)
                        .v1(targetType.getSimpleName() + ":" + method.getName())
                        .v2("")
                        .addAttachment("key", k)
                        .log();
                return Meta.build("", targetType.getSimpleName(), method.getName());
            }
            //默认值
            String group = monitor.group();
            String biz = monitor.biz().isEmpty() ? targetType.getSimpleName() : monitor.biz();
            String action = monitor.action().isEmpty() ? method.getName() : monitor.action();
            boolean printParam = monitor.printParam();
            boolean printResult = monitor.printResult();

            //织入器
            AnnotationWeaver monitorWeaver = null;
            Class<? extends AnnotationWeaver> weaverType = monitor.weaver();
            if (!weaverType.equals(AnnotationWeaver.class)) {
                try {
                    AnnotationWeaver weaver = weaverType.newInstance();
                    String[] configs = monitor.weaverConfig();
                    Map<String, String> cfg = new HashMap<>();
                    for (String config : configs) {
                        if (config == null || config.isEmpty()) {
                            continue;
                        }
                        int i = config.indexOf("=");
                        if (i < 0) {
                            throw new IllegalArgumentException("weaverConfig must be key=valStr");
                        }
                        String ck = config.substring(0, i);
                        String cv = config.substring(i + 1);
                        cfg.put(ck, cv);
                    }
                    weaver.init(group, biz, action, printParam, printResult, cfg);
                    monitorWeaver = weaver;
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                            .v1(biz + ":" + action)
                            .v2("instanceWeaver")
                            .addAttachment(MonitorInfo.ERROR, e)
                            .log();
                }
            }

            LinkLogUtil.newTagLog(TAG_PARSE_MONITOR_CONFIG)
                    .v1(biz + ":" + action)
                    .v2(group)
                    .addAttachment("key", k)
                    .addAttachment("monitorWeaver", monitorWeaver)
                    .log();

            return Meta.build(group, biz, action, printParam, printResult, monitorWeaver);
        });
    }


    private static class Meta {

        private String group;

        private String biz;

        private String action;

        private boolean printParam;

        private boolean printResult;

        /**
         * 可能为空
         */
        private AnnotationWeaver<Object> weaver;

        public static Meta build(String group, String biz, String action) {
            return build(group, biz, action, false, false, null);
        }

        public static Meta build(String group, String biz, String action, boolean printParam, boolean printResult, AnnotationWeaver<Object> weaver) {
            Meta config = new Meta();
            config.setGroup(group);
            config.setBiz(biz);
            config.setAction(action);
            config.setWeaver(weaver);
            config.setPrintParam(printParam);
            config.setPrintResult(printResult);
            return config;
        }

        public String getGroup() {
            return group;
        }

        public void setGroup(String group) {
            this.group = group;
        }

        public String getBiz() {
            return biz;
        }

        public void setBiz(String biz) {
            this.biz = biz;
        }

        public String getAction() {
            return action;
        }

        public void setAction(String action) {
            this.action = action;
        }

        public AnnotationWeaver<Object> getWeaver() {
            return weaver;
        }

        public void setWeaver(AnnotationWeaver<Object> weaver) {
            this.weaver = weaver;
        }

        public boolean isPrintParam() {
            return printParam;
        }

        public void setPrintParam(boolean printParam) {
            this.printParam = printParam;
        }

        public boolean isPrintResult() {
            return printResult;
        }

        public void setPrintResult(boolean printResult) {
            this.printResult = printResult;
        }

        @Override
        public String toString() {
            return "Meta{" +
                    "group='" + group + '\'' +
                    ", biz='" + biz + '\'' +
                    ", action='" + action + '\'' +
                    ", printParam=" + printParam +
                    ", printResult=" + printResult +
                    ", weaver=" + weaver +
                    '}';
        }
    }


}
